﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using APIKeysLib;

namespace NetLib
{
    public static class WithingsFetcher
    {
        private static OAuthBase Auth = new OAuthBase();

        [Pure]
        public static string SignedRequest(string URL, string parameters, string token = "", string tokenSecret = "")
        {
            string normalizedURL;
            string normalizedRequestParameters;

            string signature = Auth.GenerateSignature(new Uri(URL + parameters), WithingsAPIKeys.ConsumerKey,
                WithingsAPIKeys.ConsumerSecret, token, tokenSecret, "GET", Auth.GenerateTimeStamp(),
                Auth.GenerateNonce(), out normalizedURL, out normalizedRequestParameters);

            Debug.WriteLine(signature);
            Debug.WriteLine(normalizedURL);
            Debug.WriteLine(normalizedRequestParameters);

            //string results = RESTProcessor.HttpPost(normalizedURL, normalizedRequestParameters + "&oauth_signature=" + signature);
            string results = RESTProcessor.HttpGet(normalizedURL + "?" + normalizedRequestParameters + "&oauth_signature=" + signature);

            return results;

        }

        [Pure]
        public static bool? ConfirmWithingsUserExists(string userid, string publickey)
        {
            

            string[] parameterNames = new string[3];
            string[] parameterValues = new string[3];
            parameterNames[0] = "action";
            parameterValues[0] = "getbyuserid";
            parameterNames[1] = "userid";
            parameterValues[1] = userid;
            parameterNames[2] = "publickey";
            parameterValues[2] = publickey;

            string withingsUserResult = RESTProcessor.HttpPost("http://wbsapi.withings.net/user?",
                parameterNames, parameterValues);

            WithingsAccountUserListResult accountUserListResult =
               JsonConvert.DeserializeObject<WithingsAccountUserListResult>(withingsUserResult);

            if (accountUserListResult.Status == 0)
            {
                if (accountUserListResult.body.users[0].ispublic == 1
                    || accountUserListResult.body.users[0].ispublic == 5)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (accountUserListResult.Status == 247)
            {
                return null;
            }
            else if (accountUserListResult.Status == 250)
            {
                return false;
            }




            return null;
        }




        public static List<WithingsResult> FetchWithingsResults(string userid, string publickey)
        {
            List<WithingsResult> results = new List<WithingsResult>();

            string[] parameterNames = new string[4];
            string[] parameterValues = new string[4];
            parameterNames[0] = "action";
            parameterValues[0] = "getmeas";
            parameterNames[1] = "userid";
            parameterValues[1] = userid;
            parameterNames[2] = "publickey";
            parameterValues[2] = publickey;
            parameterNames[3] = "devtype";
            parameterValues[3] = "1";

            string withingsMeasurments = RESTProcessor.HttpPost("http://wbsapi.withings.net/measure?", parameterNames, parameterValues);

            WithingsMeasurmentResult withingsMeasureResult = JsonConvert.DeserializeObject<WithingsMeasurmentResult>(withingsMeasurments);

            foreach (var current in withingsMeasureResult.body.measuregrps.Where(y => y.attrib == 0 || y.attrib == 2))
            {
                var weight = current.measures.SingleOrDefault(x => x.type == 1);
                var fatRatio = current.measures.SingleOrDefault(z => z.type == 6);
                var fatMass = current.measures.SingleOrDefault(w => w.type == 8);

                results.Add(
                    new WithingsResult(current.GimmeADateTime(),
                    (weight != null) ? (double?)weight.GimmeAWeightInPounds() : null,
                    (fatRatio != null) ? (double?)fatRatio.GimmeAFatRatio() : null,
                    (fatMass != null) ? (double?)fatMass.GimmeAFatMassWeight() : null));
            }

            return results;
        }

        [Obsolete("This apparently no longer works, due to API Changes.")]
        public static List<WithingsAccountUsers> FetchWithingsAccountUserList(string email, string password)
        {
            List<WithingsAccountUsers> results = new List<WithingsAccountUsers>();

            string hash = BuildUserHash(email, password);

            string parameters = string.Format("action=getuserslist&email={0}&hash={1}", email, hash);

            string withingsAccountUserList = RESTProcessor.HttpPost("http://wbsapi.withings.net/account?",
                parameters);

            WithingsAccountUserListResult accountUserListResult =
                JsonConvert.DeserializeObject<WithingsAccountUserListResult>(withingsAccountUserList);

            if (accountUserListResult.Status != 0)
            {
                //something went wrong
                return results;//should be empty
            }



            foreach (var current in accountUserListResult.body.users)
            {
                results.Add(new WithingsAccountUsers()
                {
                    Name = current.firstname + " " + current.lastname,
                    shortname = current.shortname,
                    id = current.id,
                    publickey = current.publickey,
                    ispublic = current.ispublic
                });
            }

            return results;
        }

        [Pure]
        public static string BuildUserHash(string email, string password)
        {
            string results;
            WithingsOnceResult once = JsonConvert.DeserializeObject<WithingsOnceResult>(
                RESTProcessor.HttpGet("http://wbsapi.withings.net/once?action=get"));

            StringBuilder builder = new StringBuilder();
            builder.Append(email.ToLower());
            builder.Append(':');
            builder.Append(MD5Hasher(password));
            builder.Append(':');
            builder.Append(once.body.once);

            results = MD5Hasher(builder.ToString());


            return results;
        }

        [Pure]
        public static string MD5Hasher(string toHash)
        {
            string rethash = "";
            try
            {
                MD5 md5 = MD5.Create();
                ASCIIEncoding encoder = new ASCIIEncoding();
                md5.ComputeHash(Encoding.Default.GetBytes(toHash));
                rethash = BytesToHexString(md5.Hash); //Convert.ToBase64String( 
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error in HashCode: " + ex.Message);
            }


            return rethash;
        }

        [Pure]
        public static string BytesToHexString(byte[] bytes)
        {
            var chars = Encoding.Default.GetChars(bytes);
            StringBuilder build = new StringBuilder();

            foreach (byte cur in bytes)
            {
                build.Append(cur.ToString("x2").ToLower());
            }
            return build.ToString();
        }
    }


    #region Withings measurement data classes

    internal class WithingsMeasurmentResult
    {
        public int Status;
        public WithingsMeasurementResultBody body;
    }
    internal class WithingsMeasurementResultBody
    {
        public WithingsMeasurementResultBodyMeasurGrps[] measuregrps;
    }
    internal class WithingsMeasurementResultBodyMeasurGrps
    {
        public int grpid;
        public int attrib;
        public Int64 date;
        public int category;
        public WithingsMeasurementResultBodyMeasurGrpsMeasures[] measures;

        public override string ToString()
        {
            DateTime day = new DateTime(1970, 1, 1);
            day.AddDays(date);
            return day.ToString("s");
        }
        public DateTime GimmeADateTime()
        {
            DateTime day = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            day = day.AddSeconds(date) + TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);

            return day;
        }


    }
    internal class WithingsMeasurementResultBodyMeasurGrpsMeasures
    {
        public int value;
        public int type;
        public int unit;

        #region methods
        public override string ToString()
        {
            return
                ((double)value * Math.Pow(10.0, (double)unit) * 2.20462262).ToString()
                + " lbs";

        }
        public double GimmeAWeightInPounds()
        {
            return
                ((double)value * Math.Pow(10.0, (double)unit) * 2.20462262);
        }
        public double GimmeAFatRatio()
        {
            return
                ((double)value * Math.Pow(10.0, (double)unit) * 100);
        }
        public double GimmeAFatMassWeight()
        {
            return
                ((double)value * Math.Pow(10.0, (double)unit) * 2.20462262);
        }
        #endregion //methods
    }


    public class WithingsResult : IWeightResults
    {
        #region Data and Properties
        private DateTime _date;
        public DateTime Date
        {
            get
            {
                return _date;
            }
            set
            {
                _date = value;
            }
        }
        private double _weight;
        public double Weight
        {
            get
            {
                return _weight;
            }
            set
            {
                _weight = value;
            }
        }
        private double? _fatRatio;
        public double? FatRatio
        {
            get
            {
                return _fatRatio;
            }
            set
            {
                _fatRatio = value;
            }
        }
        private double? _fatMassWeight;
        public double? FatMassWeight
        {
            get
            {
                return _fatMassWeight;
            }
            set
            {
                _fatMassWeight = value;
            }
        }
        #endregion //Data and Properties

        #region constructors
        public WithingsResult()
        {
        }
        public WithingsResult(DateTime date, double? weight)
        {
            Date = date;
            Weight = (double)weight;
        }
        public WithingsResult(DateTime date, double? weight, double? fatratio, double? fatmass)
        {
            Date = date;
            Weight = (double)weight;
            FatRatio = fatratio;
            FatMassWeight = fatmass;
        }
        #endregion //constructors

        #region Overrides
        public override string ToString()
        {
            return Date.ToString("s") + " " + Weight + " lbs";
        }
        #endregion //Overrides
    }

    #endregion //Withings measurement data classes

    #region Withings Account Users List data classes
    internal class WithingsAccountUserListResult
    {
        public int Status;
        public WithingsAccountUsersListBody body;
    }
    internal class WithingsAccountUsersListBody
    {
        public WithingsAccountUsersListUsers[] users;
    }
    internal class WithingsAccountUsersListUsers
    {
        public int id;              //The user identifier
        public string firstname;    //The user's firstname, as an UTF-8 encoded string
        public string lastname;     //The user's lastname, as an UTF-8 encoded string
        public string shortname;    //The user's shortname
        public bool gender;         //The user's gender (0 for male, 1 for female)
        public byte fatmethod;      //Byte indicating the Body Composition Formula in use
        public int birthdate;       //The user's birthdate in Epoch format (number of seconds since January 1st, 1970)
        public int ispublic;        //A bitmask indicating the types of measures made public by the user :
        //value | b7 b6 b5 b4 b3 b2 b1 b0
        //    1 |                       x : body scale data
        //    4 |                 x       : blood pressure monitor data
        public string publickey;    //The user's current publickey
    }

    public class WithingsAccountUsers
    {
        public int id;
        public string Name;
        public string shortname;
        public int ispublic;
        public string publickey;
    }


    #endregion //Withings Account Users List data classes

    #region Withings Hash data classes
    public class WithingsOnceResult
    {
        public string status;
        public WithingsOnceResultBody body;
    }
    public class WithingsOnceResultBody
    {
        public string once;
    }
    #endregion //Withings Hash data classes
}